package cn.thens.kdroid.upay

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import com.tencent.mm.opensdk.constants.Build
import com.tencent.mm.opensdk.constants.ConstantsAPI
import com.tencent.mm.opensdk.modelbase.BaseReq
import com.tencent.mm.opensdk.modelbase.BaseResp
import com.tencent.mm.opensdk.modelpay.PayReq
import com.tencent.mm.opensdk.openapi.IWXAPI
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler
import com.tencent.mm.opensdk.openapi.WXAPIFactory
import io.reactivex.Observable
import io.reactivex.ObservableEmitter
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import java.io.Serializable

@Suppress("SpellCheckingInspection", "unused")
object WeChatPay : Payment, IWXAPIEventHandler {
    private var wxApi: IWXAPI? = null
    private var observer: ObservableEmitter<PayResult>? = null

    private const val CODE_SUCCESS = 0
    private const val CODE_CANCEL = -2
    private const val CODE_FAIL = -1
    private val gson by lazy { Gson() }

    fun initialize(context: Context, appId: String) {
        if (wxApi == null) {
            val api = WXAPIFactory.createWXAPI(context, appId)
            api.registerApp(appId)
            wxApi = api
        }
    }

    fun handleIntent(intent: Intent) {
        wxApi?.handleIntent(intent, this)
    }

    override fun pay(activity: Activity, orderInfo: String): Observable<PayResult> {
        val api = wxApi
        val observable = if (api == null || api.isWXAppInstalled || api.wxAppSupportAPI < Build.PAY_SUPPORTED_SDK_INT) {
            Observable.just(PayResult(PayResult.FAIL, "IWXAPI 未初始化"))
        } else {
            Observable.create<PayResult> {
                val order = gson.fromJson(orderInfo, OrderInfo::class.java)
                api.sendReq(order.toPayRequest())
                observer = it
            }
        }
        return observable
                .onErrorReturn { PayResult(PayResult.FAIL, "支付失败: ${it.message}") }
                .subscribeOn(Schedulers.io())
                .subscribeOn(AndroidSchedulers.mainThread())
    }

    override fun onResp(resp: BaseResp) {
        if (resp.type == ConstantsAPI.COMMAND_PAY_BY_WX) {
            val status = resp.errCode
            val description = resp.errStr
            observer?.onNext(when (status) {
                CODE_SUCCESS -> PayResult(PayResult.SUCCESS, "支付成功: $status")
                CODE_CANCEL -> PayResult(PayResult.CANCEL, "支付已取消: $status")
                CODE_FAIL -> PayResult(PayResult.FAIL, "支付失败: $status，$description")
                else -> PayResult(PayResult.FAIL, "支付失败: $status，$description")
            })
        }
    }

    override fun onReq(req: BaseReq) {
    }

    data class OrderInfo(
            @SerializedName("appid") val appId: String,
            @SerializedName("partnerid") val partnerId: String,
            @SerializedName("prepayid") val prepayId: String,
            @SerializedName("package") val packageValue: String,
            @SerializedName("noncestr") val nonceStr: String,
            @SerializedName("timestamp") val timestamp: String,
            @SerializedName("sign") val sign: String
    ) : Serializable {
        fun toPayRequest(): PayReq {
            return PayReq().also {
                it.appId = appId
                it.partnerId = partnerId
                it.prepayId = prepayId
                it.packageValue = packageValue
                it.nonceStr = nonceStr
                it.timeStamp = timestamp
                it.sign = sign
            }
        }
    }

    class CallbackActivity : Activity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            handleIntent(intent)
        }

        override fun onNewIntent(intent: Intent) {
            super.onNewIntent(intent)
            setIntent(intent)
            handleIntent(intent)
        }
    }
}